package control

import (
	"strings"

	"hundsun.com/hsl/hschain/contract/jvm/jvm.go/common"
	"hundsun.com/hsl/hschain/contract/jvm/jvm.go/instructions/base"
	"hundsun.com/hsl/hschain/contract/jvm/jvm.go/rtda"
	"hundsun.com/hsl/hschain/contract/jvm/jvm.go/rtda/heap"
)

func NewXReturn(d bool) *XReturn { return &XReturn{d: d} }

// Return void from method
type Return struct{ base.NoOperandsInstruction }

func (instr *Return) Execute(frame *rtda.Frame) {
	thread := frame.Thread
	thread.PopFrame()
}

// xreturn: Return XXX from method
type XReturn struct {
	base.NoOperandsInstruction
	d bool
}

func (instr *XReturn) Execute(frame *rtda.Frame) {

	if frame.Method.Class.Name != common.ESDaoClassName || frame.Method.Name != common.ESDaoGetMethodName {
		thread := frame.Thread
		currentFrame := thread.PopFrame()
		ref := currentFrame.PopL(instr.d)
		if (frame.Method.Name == "put" && frame.Method.Class.Name == "java/util/HashMap" ||
			frame.Method.Name == "add" && frame.Method.Class.Name == "java/util/ArrayList") &&
			frame.Method.ReturnType == "void" {
			//			fmt.Println("arraylist recover from parameters ",frame.GetLocalVar(1).Ref.JSToGoStr())
			return
		}
		invokerFrame := thread.TopFrame()
		invokerFrame.PushL(ref, instr.d)
		//合约方法的返回值需要写入交易回执
		className := frame.Method.Class.Name
		if !heap.IsJdkClass(className) && !heap.IsBaseContractClass(className) {
			if len(thread.MainClass) > 4 {
				mainContractName := thread.MainClass[:len(thread.MainClass)-4]
				//如果是合约方法的return并且是最终的return(如果合约方法调用的其他带返回值的合约方法，不是最终的return)
				if className == mainContractName && invokerFrame.Method.Class.Name == common.MainDaoName {
					retClassName := string(frame.Method.ReturnType)
					if strings.HasPrefix(retClassName, "L") {
						retClassName = retClassName[1 : len(retClassName)-1]
					}
					retObj := ref.Ref
					var retClass *heap.Class
					if retObj == nil {
						if heap.IsPrimitiveType(retClassName) {
							retClassName = heap.GetPrimitiveClassName(retClassName)
							retClass = thread.Runtime.BootLoader().LoadClass(retClassName)
							retObj = retClass.NewObj()
							retObj.Fields.([]heap.Slot)[0].Val = ref.Val
						}
					}

					retSlot := heap.NewRefSlot(retObj)
					objBuff, err := thread.RlpEncode(&retSlot, "")
					if err != nil {
						panic("Encode return value failed " + err.Error())
					} else {
						thread.GroupedTx.TxExecRst.Receipt.CallResult = objBuff
					}
				}
			}
		}
	}
}
